package com.tsyz.servlet.filter;

import com.tsyz.servlet.context.UserContextHolder;
import com.tsyz.servlet.domain.dto.UserDTO;
import com.tsyz.servlet.domain.dto.req.UserGetUserByIdReqDTO;
import com.tsyz.servlet.domain.dto.resq.UserGetUserByIdRespDTO;
import com.tsyz.servlet.domain.mapstruct.UserGetUserByIdRespDTOMapper;
import com.tsyz.servlet.service.UserService;
import com.tsyz.servlet.util.CookieUtil;
import com.tsyz.servlet.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;

/**
 * JWT身份认证过滤器
 *
 * <p>职责：
 * 1. 拦截所有HTTP请求，从Cookie中提取JWT令牌
 * 2. 验证令牌有效性并解析用户身份
 * 3. 构建Spring Security认证上下文
 * 4. 维护用户上下文信息（ThreadLocal）
 *
 * @author ext.zhaoke14
 * @see OncePerRequestFilter 确保每个请求只过滤一次
 */
@Component
public class TsyzJwtAuthenticationFilter extends OncePerRequestFilter {

    /**
     * 用于处理JWT令牌的生成、验证和解析的工具类实例
     */
    @Autowired
    private JwtUtil jwtUtil;

    /**
     * 用于操作用户数据的服务类实例
     */
    @Autowired
    private UserService userService;

    /**
     * 在过滤器链中执行身份验证和授权逻辑。
     * 从请求中提取JWT令牌，验证其有效性，并从中获取用户ID和用户名。
     * 然后查询数据库以获取用户详细信息，并将其存储在ThreadLocal中以供后续使用。
     * 最后清除ThreadLocal中的用户信息。
     *
     * @param request     HTTP请求对象
     * @param response    HTTP响应对象
     * @param filterChain 过滤器链对象
     * @throws ServletException 如果在过滤器链中发生错误
     * @throws IOException      如果在过滤器链中发生I/O错误
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            String token = getTokenFromRequest(request);

            // 添加空值检查，防止空指针异常
            if (token != null && jwtUtil != null && jwtUtil.validateToken(token)) {
                try {
                    // 从Token获取用户信息
                    Long userId = jwtUtil.getUserIdFromToken(token);
                    String username = jwtUtil.getUserNameFromToken(token);

                    // 查询用户信息
                    UserGetUserByIdReqDTO reqDTO = UserGetUserByIdReqDTO.builder().id(userId).build();
                    UserGetUserByIdRespDTO respDto = userService.getUserById(reqDTO);

                    if (respDto != null && 0 == respDto.getIsDelete() && respDto.getUserName().equals(username)) {
                        // 创建认证对象
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
                        SecurityContextHolder.getContext().setAuthentication(authentication);

                        // 将用户信息存储到ThreadLocal中（保持与现有代码一致）
                        UserDTO userDto = UserGetUserByIdRespDTOMapper.INSTANCE.dtoToDto(respDto);
                        UserContextHolder.setUser(userDto);
                    }
                } catch (Exception e) {
                    logger.error("JWT认证失败", e);
                }
            }

            filterChain.doFilter(request, response);
        } finally {
            // 清除ThreadLocal中的用户信息
            UserContextHolder.clear();
        }
    }

    /**
     * 从HTTP请求的Cookie中获取认证Token。
     *
     * @param request HTTP请求对象
     * @return 认证Token字符串，若未找到则返回null
     */
    private String getTokenFromRequest(HttpServletRequest request) {
        // 从Cookie中获取Token（保持与现有代码一致）
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                // 修改为与CookieUtil中一致的Cookie名称
                if (CookieUtil.TOKEN_COOKIE_NAME.equals(cookie.getName())) {
                    return cookie.getValue();
                }
            }
        }
        return null;
    }
}
